﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using PaintDotNet;
using PaintDotNet.Effects;

namespace LibFiltr
{
    public class RankFilter
    {
        protected bool[,] _kernel;
        protected int _radius;
        protected double _ratio;

        //comparison function for Bgra objects based on luminosity
        private static int CompareByLuminosity(ColorBgra a, ColorBgra b)
        {
            double al = (double)(299 * a.R + 587 * a.G + 114 * a.B) / 1000;
            double bl = (double)(299 * b.R + 587 * b.G + 114 * b.B) / 1000;
            return al.CompareTo(bl);
        }

        public RankFilter(int radius, double ratio, bool roundKernel, bool includeCenter)
        {
            _radius = radius;
            _kernel = new bool[Size, Size];
            _ratio = ratio;

            if (roundKernel)
                makeRoundKernel();
            else
                makeSquareKernel();
            if(!includeCenter)
                _kernel[(Size - 1) / 2, (Size - 1) / 2] = false;
        }

        private void makeSquareKernel()
        {
            for (int i = 0; i < Size; i++)
            {
                for (int j = 0; j < Size; j++)
                {
                    _kernel[i, j] = true;
                }
            }
        }

        private void makeRoundKernel()
        {
            for (int i = -_radius; i <= _radius; i++)
            {
                for (int j = -_radius; j <= _radius; j++)
                {
                    if (Math.Pow(Math.Pow(i, 2) + Math.Pow(j, 2), 0.5) <= _radius)
                    {
                        _kernel[i + _radius, j + _radius] = true;
                    }
                }
            }
        }

        public int Size
        {
            get { return 2 * _radius + 1; }
        }

        public ColorBgra Rank(List<ColorBgra> list)
        {
            list.Sort(CompareByLuminosity);
            return list[(int)Math.Round((list.Count - 1) * _ratio)];
        }

        public void Filter(Surface dst, Surface src, Rectangle selection, Rectangle rect)
        {
            ColorBgra CurrentPixel;
            List<ColorBgra> list = new List<ColorBgra>();
            for (int y = rect.Top; y < rect.Bottom; y++)
            {
                for (int x = rect.Left; x < rect.Right; x++)
                {
                    CurrentPixel = src[x, y];
                    for (int i = -_radius; i <= _radius; i++)
                    {
                        for (int j = -_radius; j <= _radius; j++)
                        {
                            if (_kernel[i + _radius, j + _radius])
                            {
                                // check for margins
                                int ipos = Math.Max(y + i, selection.Top), jpos = Math.Max(x + j, selection.Left);
                                ipos = Math.Min(ipos, selection.Bottom - 1); jpos = Math.Min(jpos, selection.Right - 1);
                                list.Add(src[jpos, ipos]);
                            }
                        }
                    }
                    CurrentPixel = Rank(list);
                    dst[x, y] = CurrentPixel;
                    list.Clear();
                }
            }
        }
    }
}
